///<reference path="../typings/knockout.d.ts" />
import model = require("index.model");
import mediator = require("index.mediator");
import eventmgr = require("eventmgr");

var idCtr = 0;

export class Context {
	public mdl: model.Model;
	public emgr: eventmgr.EventMgr;
	public vm: ViewModel;
}

export class ViewModel {
	public id = idCtr++;
	
	public cxt: Context;
	
	public tabs = ko.observableArray<Tab>();
	public selectedTab = ko.observable<Tab>();
	
	public userName: KnockoutObservable<string>;
	
	public startTab: StartTab;
	public kkTab: KkTab;
	public browseTab: BrowseTab;
	public helpTab: HelpTab;
	
	public isBusy = ko.observable<boolean>(false);
	public isAdmin = ko.observable<boolean>(true);
	
	public onUserNameChanged() {
		var oldKk = this.cxt.mdl.kk();
		var newKk = new model.KonsenskisteImpl();
		this.cxt.emgr.kkNeeded({ id: oldKk.id, out: newKk });
		this.cxt.mdl.kk(newKk);
	}
	
	constructor(mdl: model.Model, emgr: eventmgr.EventMgr) {
		this.cxt = <Context>{ mdl: mdl, emgr: emgr, vm: this };
		
		this.userName = mdl.user;
		this.userName.subscribe(() => this.onUserNameChanged());
		
		this.helpTab = new HelpTab();
		this.startTab = new StartTab();
		this.kkTab = new KkTab(mdl.kk, this.cxt);
		this.browseTab = new BrowseTab(mdl.topic, this.cxt)
		
		this.tabs([this.startTab, this.kkTab, this.browseTab, this.helpTab]);
		this.selectedTab(this.kkTab);
	}
}

export class Tab {
	public id = idCtr++;
	public type = "none";
	public title = ko.observable("unnamed");
}

export class StartTab extends Tab {
	constructor() {
		super()
		this.type = 'start'
		this.title('Start')
	}
}

export class HelpTab extends Tab {
	constructor() {
		super()
		this.type = 'help'
		this.title('?!')
	}
}

export class KkTab extends Tab {
	private _kk = ko.observable<KnockoutObservable<model.Konsenskiste>>();
	public kk = ko.computed<model.Konsenskiste>(() => this._kk() ? this._kk()() : null);
	
	public kkTitle = ko.computed<string>(() => this.kk() ? this.kk().title() : null);
	public kkText = ko.computed<string>(() => this.kk() ? this.kk().text() : null);
	
	public kkView = ko.computed<KkView>(() => this.kk() ? new KkView(this.kk(), this.cxt) : null);
	
	private cxt: Context;
	
	constructor(_kk: KnockoutObservable<model.Konsenskiste>, cxt: Context) {
		super();
		this.type = "kk";
		this.cxt = cxt;
		this.title('Konsenskiste');
		this._kk(_kk);
	}
}

export class BrowseTab extends Tab {
	private _topic = ko.observable<KnockoutObservable<model.Topic>>();
	public topic = ko.computed<model.Topic>(() => this._topic() ? this._topic()() : null);
	
	public parentTopicView = ko.computed<ParentTopicView>(() => this.topic() ? new ParentTopicView(this.topic(), this.cxt) : null);
	
	private cxt: Context;
	
	constructor(_topic: KnockoutObservable<model.Topic>, cxt: Context) {
		super();
		this.type = "browse";
		this.cxt = cxt;
		this.title('Themen');
		this._topic(_topic);
	}
}

export class ParentTopicView {
	private topic: model.Topic;

	public title: KnockoutComputed<string>;
	public text: KnockoutComputed<string>;
	
	public breadcrumb: KnockoutComputed<model.Topic[]>;
	public children: KnockoutObservableArray<model.Topic>;
	public kks: KnockoutObservableArray<model.Konsenskiste>;
	
	public childTopicView = (tpc: model.Topic) => new OverallTopicView(tpc, model.TopicRole.Child, this.cxt);
	public breadcrumbTopicView = (tpc: model.Topic) => new OverallTopicView(tpc, model.TopicRole.Breadcrumb, this.cxt);
	public kkView = (kk: model.Konsenskiste) => new TopicKkView(kk, this.cxt);
	
	public newKk = {
		isVisible: ko.observable<boolean>(false),
		title: ko.observable<string>(),
		text: ko.observable<string>(),
		onClick: () => { this.newKk.isVisible(!this.newKk.isVisible()) },
		onSubmit: () => {
			var kk = new model.KonsenskisteImpl();
			this.cxt.emgr.kkSubmitted({ title: this.newKk.title(), text: this.newKk.text(), parent: this.topic, out: kk });
			this.cxt.mdl.kk(kk);
			this.cxt.vm.selectedTab(this.cxt.vm.kkTab);
		}
	};
	
	private cxt: Context;
	
	constructor(topic: model.Topic, cxt: Context) {
		this.cxt = cxt;
		this.topic = topic;
		this.title = ko.computed(() => this.topic.id
			? ( this.topic.title() ? this.topic.title() : this.topic.text() )
			: "[root]"
		);
		this.text = ko.computed(() => this.topic.title() ? this.topic.text() : null);
		this.children = topic.children;
		this.breadcrumb = ko.computed(() => cxt.mdl.topicBreadcrumb().slice(0,-1));
		this.kks = topic.kks;
	}
}

export class TopicKkView {
	private kk: model.Konsenskiste;
	public caption: KnockoutComputed<string>;
	
	public click = () => { this.cxt.mdl.kk(this.kk); this.cxt.emgr.kkNeeded({ id: this.kk.id, out: this.kk }); this.cxt.vm.selectedTab(this.cxt.vm.kkTab) };
	
	private cxt: Context;
	
	constructor(kk: model.Konsenskiste, cxt: Context) {
		this.cxt = cxt;
		this.kk = kk;
		this.caption = ko.computed<string>(() => this.kk.title() ? this.kk.title() : this.kk.text());
	}
}

export class OverallTopicView {
	public topic: model.Topic;

	public title: KnockoutObservable<string>;
	public text: KnockoutObservable<string>;
	public caption: KnockoutComputed<string>;
	
	public role: model.TopicRole;
	
	public click = () => {
		this.cxt.mdl.selectTopic(this.topic, this.role);
		this.cxt.emgr.topicNeeded({ id: this.topic.id, out: this.topic })
	};
	
	private cxt: Context;
	
	constructor(topic: model.Topic, role: model.TopicRole, cxt: Context) {
		this.cxt = cxt;
		this.topic = topic;
		this.role = role;
		this.title = topic.title;
		this.text = topic.text;
		this.caption = ko.computed<string>(() => {
			if(this.topic.id)
				return this.title() + ': ' + this.text();
			else
				return "[root]";
		});
	}
}

export interface QkElementView {
	id: number;
	type: string;
	title: KnockoutComputed<string>;
	text: KnockoutComputed<string>;
	
	rating: KnockoutObservable<string>;
	likeRating: KnockoutComputed<string>;
	stronglikeSum: KnockoutObservable<number>;
	likeSum: KnockoutObservable<number>;
	neutralSum: KnockoutObservable<number>;
	dislikeSum: KnockoutObservable<number>;
	strongdislikeSum: KnockoutObservable<number>;
}

export class QkMainElementViewBase implements QkElementView {
	public id = idCtr++;
	public type = 'qk-none';
	
	public title: KnockoutComputed<string>;
	public text: KnockoutComputed<string>;

	public rating: KnockoutObservable<string>;
	public likeRating: KnockoutComputed<string>;
	
	public stronglikeSum: KnockoutObservable<number>;
	public likeSum: KnockoutObservable<number>;
	public neutralSum: KnockoutObservable<number>;
	public dislikeSum: KnockoutObservable<number>;
	public strongdislikeSum: KnockoutObservable<number>;
	
	public comments: KnockoutObservableArray<model.QkComment>;
	public showAllComments = ko.observable(false);
	
	public cmtView = (cmt: model.QkComment) => new CmtView(cmt, this._cxt);
	public disabled = ko.observable<boolean>();
	public newCommentText = ko.observable<string>();
	
	public clickRating = (elem: model.QkElement, rating: string) => setTimeout.bind(null, () => {
		this._cxt.emgr.rated({ postId: this.qk.id, rating: rating, then: rating => { console.log(rating); this.qk.rating(rating) } });
	}, 0);
	
	public submitComment_onClick = () => {
		console.log({ text: this.newCommentText(), parent: this.qk })
		this._cxt.emgr.cmtSubmitted({ text: this.newCommentText(), parent: this.qk });
		this.newCommentText('');
	}
	
	public context: KnockoutObservable<string>;
	public showContext = ko.observable(false);
	
	private qk: model.QkMainElement;
	private _cxt: Context;
	
	constructor(qk: model.QkMainElement, cxt: Context) {
		this.qk = qk;
		this._cxt = cxt;
		
		this.comments = qk.comments;
		this.context = qk.context;
		this.rating = qk.rating;
		this.likeRating = qk.likeRating;
		this.stronglikeSum = qk.stronglikeSum;
		this.likeSum = qk.likeSum;
		this.neutralSum = qk.neutralSum;
		this.dislikeSum = qk.dislikeSum;
		this.strongdislikeSum = qk.strongdislikeSum;
		
		this.showAllComments.subscribe(val => {
			if(val) {
				if(this.comments().length && !this.comments()[0].text())
					cxt.emgr.commentsNeeded({ parent: this.qk });
			}
		});
	}
}

export class KkView extends QkMainElementViewBase {
	public type = 'kk';
	
	private kk = ko.observable<model.Konsenskiste>();
	public title = ko.computed<string>(() => this.kk() ? this.kk().title() : null);
	public text = ko.computed<string>(() => this.kk() ? this.kk().text() : null);
	public children: KnockoutObservableArray<model.Kernaussage>;
	
	public kaView = (ka: model.Kernaussage) => new KaView(ka, this.cxt);
	
	public kaForm = {
		isVisible: ko.observable<boolean>(false),
		title: ko.observable<string>(),
		text: ko.observable<string>(),
		context: ko.observable<string>(),
		onClick: () => { this.kaForm.isVisible(!this.kaForm.isVisible()) },
		onSubmit: () => {
			var ka = new model.KernaussageImpl();
			this.cxt.emgr.kaSubmitted({ title: this.kaForm.title(), text: this.kaForm.text(), context: this.kaForm.context(), parent: this.kk(), out: ka });
			this.kk().children.push(ka);
		}
	};
	
	private cxt: Context;
	
	constructor(kk: model.Konsenskiste, cxt: Context) {
		super(kk, cxt);
		this.cxt = cxt;
		this.kk(kk);
		this.children = kk.children;
	}
}

export class KaView extends QkMainElementViewBase {
	public type = 'ka';

	private ka = ko.observable<model.Kernaussage>();
	public title = ko.computed<string>(() => this.ka() ? this.ka().title() : null);
	public text = ko.computed<string>(() => this.ka() ? this.ka().text() : null);
	
	public remove_onClick = () => {
		if( confirm('Soll diese Kernaussage wirklich entfernt werden?') ) {
			this.cxt.emgr.removeKa({ ka: this.ka() });
		}
	}
	
	private cxt: Context;
	
	constructor(ka: model.Kernaussage, cxt: Context) {
		super(ka, cxt);
		this.cxt = cxt;
		this.ka(ka);
	}
}

export class CmtView implements QkElementView {
	public id = idCtr++;
	public type = 'cmt';
	
	private cmt = ko.observable<model.QkComment>();
	public title = ko.computed(() => this.cmt() ? this.cmt().title() : null);
	public text = ko.computed(() => this.cmt() ? this.cmt().text() : null);
	
	public remove_onClick = () => {
		if( confirm('Soll dieser Kommentar wirklich aus der Diskussion entfernt werden? (Er wird aber nicht gelöscht!)') ) {
			this.cxt.emgr.removeCmt({ cmt: this.cmt() });
		}
	}
	
	public like_onClick = (newRating: string) => setTimeout.bind(null, () => {
		this.cxt.emgr.rated({ postId: this.cmt().id, rating: newRating, then: rating =>  this.cmt().rating(rating) });
	}, 0);
	
	public rating: KnockoutObservable<string>;
	public likeRating: KnockoutComputed<string>;
	public stronglikeSum: KnockoutObservable<number>;
	public likeSum: KnockoutObservable<number>;
	public neutralSum: KnockoutObservable<number>;
	public dislikeSum: KnockoutObservable<number>;
	public strongdislikeSum: KnockoutObservable<number>;
	
	private cxt: Context;
	
	constructor(cmt: model.QkComment, cxt: Context) {
		this.cxt = cxt;
		this.cmt(cmt);
		this.rating = cmt.rating;
		this.likeRating = cmt.likeRating;
		this.stronglikeSum = cmt.stronglikeSum;
		this.likeSum = cmt.likeSum;
		this.neutralSum = cmt.neutralSum;
		this.dislikeSum = cmt.dislikeSum;
		this.strongdislikeSum = cmt.strongdislikeSum;
	}
}